package agent.ccs

import scala.collection.mutable.HashMap
import cwb.cwb

/**
 * An environment stores bindings of agents and relabelings and provides number of access support
 * to help detect bad recursion.
 */
class Environment {
  // Stores agent variables.
  private var agentMap = new HashMap[Symbol, CCSAgent]
   
  // Stores relabel variables
  private var relabelMap = new HashMap[Symbol, LitRelabel]
  
  // Holds number of agent variable accesses for each variable.
  private var accessMap = new HashMap[Symbol, Int]
  
  /**
   * Registers a lookup of the given agent variable to have taken place and returns the number of
   * times the variable has been looked up. Used to detect bad recursive definitions.
   * @param symbol variable to register lookup from.
   * @return number of times variable has been looked up.
   */
  def getAccesses(symbol: Symbol): Int = {
    accessMap.get(symbol) match {
      case Some(x) => {
        accessMap.put(symbol, x + 1)
        x
      }
      case None => { 
        accessMap.put(symbol, 1)
        0
      }
    }
  }
  
  // Used to reset the max lookup counters
  def clearAccesses() {
    accessMap.clear()
  }
  
    
  /**
   * Returns the relabel bound to the given symbol if found and throws an unbound exception otherwise
   * @param sym relabel variable name to look up
   * @throws Unbound exception
   */
  def getRelabel(sym: Symbol): LitRelabel = {
    relabelMap.get(sym) match {
    case Some(x) => x
    case None => throw new Unbound("The free identifier " + sym.name + " makes it impossible to compute this result") 
    }
  }
   
  /**
   * Adds to the environment a link from the given symbol to the given relabel.
   * @param id variable name
   * @param relabel literal relabel to be bound.
   */
  def putRelabel(id: Symbol, relabel: LitRelabel) {
    relabelMap.put(id, relabel)
  }
  
  /**
   * Removes the given relabel variable.
   * @param id relabel variable to be cleared
   */
   def clearRelabel(id: Symbol) = relabelMap.remove(id)
   

   /**
    * Returns Some(Agent) if the given symbol is bound and None otherwise
    * @param id variable to be looked up
    * @returns Some(Agent) or None, depending on existence.
    */
   def getAgent(id: Symbol): Option[CCSAgent] = agentMap.get(id)
   
   // Shortcut for just sending a string to retrieve an agent.
   def getAgent(id: String): Option[CCSAgent]  = getAgent(Symbol(id))
   
   def agentsAsList: List[(Symbol, CCSAgent)] = agentMap.toList
   
   /**
    * Returns the relabel hashmap as a list of (variable name, relabel) pairs
    * @return a list version of the relabel map
    */
   def relabelsAsList: List[(Symbol, LitRelabel)] = relabelMap.toList

   
   /**
    * Creates an environmental binding from the given symbol to the given agent.
    * @param sym variable name
    * @param agent agent to be bounded
    */
   def insertBinding(id: String, agent: CCSAgent): Unit = {
     insertBinding(Symbol(id), agent)
   }
   
   /**
    * Creates an environmental binding from the given symbol to the given agent.
    * @param sym variable name
    * @param agent agent to be bounded
    */
   def insertBinding(sym: Symbol, agent: CCSAgent): Unit = {
     cwb.dbprintln("Parser: Adding agent " + sym.name)
     agentMap.remove(sym)
     agentMap.put(sym, agent)
   }
   
   
   /**
    * Removes the binding for the given id
    * @param id binding symbol to be cleared
    */
   def clearBinding(id: Symbol): Unit = {
     agentMap.remove(id)
   }
   
    /**
    * Removes the binding for the given id
    * @param id binding symbol to be cleared
    */
   def clearBinding(id: String):Unit = clearBinding(Symbol(id))
   
}
